#include "info.h"

int a = 1;

void init_lcd(struct lcd_info *lcdinfo)//初始化LCD
{

    lcdinfo->fd = open("/dev/fb0",O_RDWR);
    if(lcdinfo->fd == -1)
    {
        perror("打开LCD失败");
        exit(0);
    }

    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    //获取LCD硬件参数
    ioctl(lcdinfo->fd, FBIOGET_VSCREENINFO, &vinfo);
    ioctl(lcdinfo->fd, FBIOGET_FSCREENINFO, &finfo);
    ioctl(lcdinfo->fd, FBIOGET_VSCREENINFO, &lcdinfo->vinfo);

    lcdinfo->width = vinfo.xres;
    lcdinfo->height = vinfo.yres;
    lcdinfo->bpp = vinfo.bits_per_pixel;
    int screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

    //内存映射
    lcdinfo->fd_mem = mmap(NULL,screensize*2,PROT_READ | PROT_WRITE,MAP_SHARED,lcdinfo->fd,0); 
    if(lcdinfo->fd_mem == MAP_FAILED)
    {
        perror("内存映射失败");
        exit(0);
    }
    printf("lcdwidth=%d,lcdheight=%d,bpp=%d\n",lcdinfo->width,lcdinfo->height,lcdinfo->bpp);
}



void print_init(void)//初始界面
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
     
    // int lcd=open("/dev/fb0",O_RDWR);
 
    // 申请一块映射内存
    // char *p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 

    int screensize = 800 * 480 * 4;
    memset(lcdinfo.fd_mem,0x00000000,screensize*2);   
    lcdinfo.vinfo.xoffset = 0;
    lcdinfo.vinfo.yoffset = 0;
    if (ioctl(lcdinfo.fd, FBIOPAN_DISPLAY, &(lcdinfo.vinfo)) == -1)
    {
        perror("jpb  2 ioctl failed\n");
        exit(1);
    }

    // bzero(lcdinfo.fd_mem,size(lcdinfo.fd));
 
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(280,80,4,0x000000ff);//宽、高、像素位数、

    fontSetSize(f, 40);//字库与字体大小
    fontPrint(f,bm,0,0,"右滑-->相册",0x00ff0000,280);     
    showbitmap(bm,lcdinfo.fd_mem,120,220);
    bzero(bm->map,bm->height*bm->width*bm->byteperpixel); 

    fontPrint(f,bm,0,0,"标牌<--左滑",0x00ff0000,280);     
    showbitmap(bm,lcdinfo.fd_mem,480,220);
    bzero(bm->map,bm->height*bm->width*bm->byteperpixel); 

}



int touchPanel_init(void)//按压位置与功能
{
    int fp = open("/dev/input/event0",O_RDWR);
    if(fp == -1)
    {
        perror("打开触摸板失败：");
        exit(0);
    }

    bool xdone = false;
    bool ydone = false;
    int x;
    int y;    

    struct input_event buf;

    while(1)
    {
        read(fp,&buf,sizeof(buf));

        if(buf.type == EV_KEY && buf.code == ABS_X)
        {
            if(!xdone)
            {
                x = buf.value; 
                xdone = true;
                printf("x=%d",x);               
            }
        }

        if(buf.type == EV_KEY && buf.code == ABS_Y)
        {
            if(!ydone)
            {
                y = buf.value;  
                ydone = true; 
                printf("y=%d",y);             
            }
        }

        if(buf.type == EV_KEY && buf.code == BTN_TOUCH && buf.value == 0)
            break;

    }

        if((x >= 160 && x <= 240) && (y >= 200 && y<=280))
        {
            return 1;//相册功能
        }

        if((x >= 560 && x <= 640) && (y >= 200 && y <=280))
        {
            return 2;//数码标牌功能
        }

        if((x >= 720 && x <= 800) && (y >= 440 && y <= 480))
        {
            return 3;//退出功能
        }
    
}



int touchPanel(void)//触摸屏
{
    int fp = open("/dev/input/event0",O_RDWR);

    bool xdone = false;
    bool ydone = false;
    struct input_event buf;
    int x1,y1,x2,y2;  

    while(1)
    {
        read(fp,&buf,sizeof(buf));

        if(buf.type == EV_ABS && buf.code == ABS_X)// x坐标
        {
            if(!xdone)
            {
                x1 = buf.value;
                xdone = true;
            }
            else
                x2 = buf.value;
        }
        if(buf.type == EV_ABS && buf.code == ABS_Y)// y坐标
        {
            if(!ydone)
            {
                y1 = buf.value;
                ydone = true;
            }
            else
                y2 = buf.value;
        }
        
        if(buf.type == EV_KEY && buf.code == BTN_TOUCH && buf.value == 0)
            break;
    }

        x1 = x1*800/1024;
        y1 = y1*480/600;
        x2 = x2*800/1024;
        y2 = y2*480/600;

        printf("起始:%d %d\n",x1,y1);
        printf("最终:%d %d\n",x2,y2);

        if(x2 - x1 > 50 && abs(y2 - y1)<50)
        {
            printf("右滑\n");            
            return 1;
        }

        if(x1 - x2 > 50 && abs(y2 - y1)<50)
        {
            if(x1 >= 750 && x1 <= 800)
            {
                printf("返回");
                return 3;                
            }

            printf("左滑\n");              
            return 2;            
        }
}

bitmap_list init_bitmap()//初始头节点
{
    bitmap_list new = calloc(1,sizeof(bitmap1));
    if(new != NULL)
    {
        new->next = new;
        new->prve = new;
    }
    return new;
}


// 参数说明：
//   jpgdata: jpg图片数据
//   jpgsize: jpg图片大小
// 返回值说明：
//   成功：
//   失败：NULL
void jpg2rgb(const char *jpgdata, size_t jpgsize,bitmap_list jpginfo)
{

    // 1，声明解码结构体，以及错误管理结构体
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    // 2，使用缺省的出错处理来初始化解码结构体
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);

    // 3，配置该cinfo，使其从 jpgdata 中读取jpgsize个字节
    //    这些数据必须是完整的JPEG数据
    jpeg_mem_src(&cinfo, jpgdata, jpgsize);

    // 4，读取JPEG文件的头，并判断其格式是否合法
    if(!jpeg_read_header(&cinfo, true))
    {
        fprintf(stderr, "jpeg_read_header failed: "
            "%s\n", strerror(errno));
        return;
    }

    // 5，开始解码
    jpeg_start_decompress(&cinfo);

    // 6，获取图片的尺寸信息
    jpginfo->width  = cinfo.output_width;
    jpginfo->height = cinfo.output_height;
    jpginfo->bpp    = cinfo.output_components*8;
    jpginfo->size   = jpginfo->width * jpginfo->height * jpginfo->bpp/8;

    int row_stride = jpginfo->width * jpginfo->bpp/8;

    // 7，根据图片的尺寸大小，分配一块相应的内存rgbdata
    //    用来存放从jpgdata解码出来的图像数据
    unsigned long linesize = cinfo.output_width * cinfo.output_components;
    unsigned long rgbsize  = linesize * cinfo.output_height;
    jpginfo->rgb = calloc(1, rgbsize);

    // 8，循环地将图片的每一行读出并解码到rgb_buffer中
    int line = 0;
    while(cinfo.output_scanline < cinfo.output_height)
    {
        unsigned char *buffer_array[1];
        buffer_array[0] = jpginfo->rgb + cinfo.output_scanline * linesize;
        jpeg_read_scanlines(&cinfo, buffer_array, 1);
    }

    // 9，解码完了，将jpeg相关的资源释放掉
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
}

void load_jpg(const char *jpgfile, bitmap_list node)
{
    FILE *fp = fopen(jpgfile, "r");
    if (!fp) 
    {  
        perror("fopen");  
        exit(0);  
    }

    // 读取jpg数据
    struct stat finfo;
    stat(jpgfile, &finfo);
    char *jpgdata = calloc(1, finfo.st_size);

    // 加载JPG数据
    int n, total=0, jpgsize=finfo.st_size;
    while(jpgsize > 0)
    {
        n = fread(jpgdata+total, 1, jpgsize, fp); // n就是实际读到的字节数

        jpgsize -= n;
        total   += n;
    }

    // 调用jpg2rgb
    jpg2rgb(jpgdata, finfo.st_size, node);

    fclose(fp);
}

void show_init(bitmap_list new, int num)//显示图片
{
    //清屏
    int screensize = 800 * 480 * 4;
    memset(lcdinfo.fd_mem + screensize * num,0x00000000,screensize);

    //大图片缩小
    int display_w = new->width;
    int display_h = new->height;
    int start = 0;
    float scale = 0;
    if((new->width > lcdinfo.width) || (new->height > lcdinfo.height))
    {
        float scale_w = (float)new->width  / lcdinfo.width;
        float scale_h = (float)new->height / lcdinfo.height;
        scale   = scale_w > scale_h ? scale_w : scale_h;

        display_w = new->width  / scale;
        display_h = new->height / scale;

        start = 1;
    }

    //小图片居中
    char *tem = lcdinfo.fd_mem;
    int x=0,y=0;
    int wlcd = lcdinfo.width;
    int hlcd = lcdinfo.height;
    if((display_w <= lcdinfo.width) && (display_h <= lcdinfo.height))
    {
        y = (lcdinfo.height - display_h)/2;
        x = (lcdinfo.width  - display_w )/2;
        tem = lcdinfo.fd_mem + (800*(y-1)+x) * 4;

        wlcd = lcdinfo.width  - x;
        hlcd = lcdinfo.height - y;        
    }

    //显示bmp图片
    if(strstr(new->name,".bmp"))
    {
        // 计算无效字节
        int linesize = new->info.width * new->info.bit_count/8;
        int pad = (4 - linesize % 4) % 4; // 0 ~ 3

        //图片信息存入
        new->rgb = calloc(1,new->size);
        int n,bmpsize = new->size,total=0;   

        //读取RGB信息
        FILE *fp = fopen(new->name, "rb");     
        while(bmpsize>0)
        {
            n = fread(new->rgb,1,bmpsize,fp);
            bmpsize -= n;
            total += n;
        }
        // 取最底一行的指针
        char *rgb = new->rgb + (linesize + pad) * (new->info.height - 1);

        // 4. 按照BMP的特性将RGB显示出来
        int w, h;
        char *src_pixel; 
        src_pixel = rgb;               
        for(h = 0; h < display_h && h < hlcd; h++)
        {
            unsigned lcd_offset = h * lcdinfo.width * 4;

            // 显示一行
            for(w = 0; w < display_w && w < wlcd; w++)
            {
                int src_x = w * scale;
                int src_y = new->info.height - 1 - h * scale;

                if(start == 1)//判断图片是否需要缩小
                src_pixel = new->rgb + (src_y * (new->info.width * new->bpp + pad))+ (src_x * new->bpp) - w*3;

                memcpy(tem + w * 4 + lcd_offset + (screensize * num), src_pixel + w*3, 3);
            }
            if(start != 1)
            src_pixel -= (linesize + pad);
        }

        lcdinfo.vinfo.xoffset = 0;
        lcdinfo.vinfo.yoffset = lcdinfo.height * num;
        if (ioctl(lcdinfo.fd, FBIOPAN_DISPLAY, &(lcdinfo.vinfo)) == -1)
        {
            perror("jpb  2 ioctl failed\n");
            exit(1);
        }
        fclose(fp);          
    }
    //显示jpg图片
    else if(strstr(new->name,".jpg"))
    {

        // 4. LCD显示出来
        int w, h;
        char *src_pixel; 
        src_pixel = new->rgb;
        for(h=0; h < display_h && h< lcdinfo.height ; h++)
        {
            unsigned lcd_offset = h * lcdinfo.width*4;
            unsigned rgb_offset = h * new->width  * 3;
            
            // 显示一行
            for(w=0; w < display_w && w< lcdinfo.width ; w++)
            {
                int src_x = w * scale;
                int src_y = h * scale;

                if(start == 1)//判断图片是否需要缩小
                src_pixel = new->rgb + (src_y * new->width * (new->bpp / 8))+ (src_x * new->bpp / 8) - w*3 - rgb_offset;

                memcpy(tem + (screensize * num) + w*4 + lcd_offset + 2, src_pixel + w*3 + rgb_offset + 0, 1); 
                memcpy(tem + (screensize * num) + w*4 + lcd_offset + 1, src_pixel + w*3 + rgb_offset + 1, 1); 
                memcpy(tem + (screensize * num) + w*4 + lcd_offset + 0, src_pixel + w*3 + rgb_offset + 2, 1); 
            }
        }
        //刷新
        lcdinfo.vinfo.xoffset = 0;
        lcdinfo.vinfo.yoffset = lcdinfo.height * num;
        if (ioctl(lcdinfo.fd, FBIOPAN_DISPLAY, &(lcdinfo.vinfo)) == -1)
        {
            perror("jpb  2 ioctl failed\n");
            exit(1);
        }
        // close(lcdinfo.fd);        
    }
}

void lcd_show(int num)//显示区域
{
    lcdinfo.vinfo.xoffset = 0;
    lcdinfo.vinfo.yoffset = lcdinfo.height * num;
}

bitmap_list new_node(FILE *fp,struct dirent *ep)//建立新节点
{
    bitmap_list new = calloc(1,sizeof(bitmap1));
    strcpy(new->name,ep->d_name);//文件名称
    if(new == NULL)
    {
        perror("创建失败");
        exit(0);
    }    
    else if(new != NULL)
    {
        new->prve = new;
        new->next = new;

        if(strstr(ep->d_name, ".bmp"))
        {
        new->fp = fp;
        //读图片信息
        fread(&new->headar, sizeof(new->headar), 1, fp);
        fread(&new->info,   sizeof(new->info),   1, fp);
        new->height = new->info.height;
        new->width = new->info.width;
        new->bpp = new->info.bit_count/8;
        //图片大小
        new->size = new->height * new->width * new->bpp;         
        }
    }
    return new;
}


bool is_empty(bitmap_list head)//判断节点个数
{
    return head->size == 0;
}

bool del_node(bitmap_list head)//删除节点
{
    if(is_empty(head))
        return false;
        
    for(bitmap_list p=head->next ; p != head ; )
    {
        bitmap_list temp = p;
        p->next=p->next->next;
        free(temp);
        head->size--;
    }
}

bool add_bitmap_node(bitmap_list head,bitmap_list new)//添加节点
{
    new->prve = head;
    new->next = head->next;

    head->next = new;
    new->next->prve = new;

    head->size++;
}

void show(bitmap_list head)//遍历输出信息
{
    for(bitmap_list p=head->next;p!= head;p=p->next)
    {      
        printf("%s:%d*%d\n",p->name,p->height,p->width); 
    }
}

int size(int lcd)
// 计算LCD屏幕所需显存大小的函数，参数为LCD设备文件描述符，返回值为所需显存大小
{
    struct fb_var_screeninfo vinfo; // 显卡设备的可变属性结构体
    ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo); // 获取可变属性
    // 获得当前显卡所支持的虚拟区显存大小
    unsigned long width  = vinfo.xres;
    unsigned long height = vinfo.yres;
    unsigned long bpp    = vinfo.bits_per_pixel;
    unsigned long screen_size = width * height * bpp/8;
    return screen_size;
}
 
void showbitmap(bitmap *bm, char *p, int x, int y)
// 显示位图函数，将位图bm显示在内存映射的LCD缓冲区p上，起始位置为(x, y)
{    
    for(int j=0;j<bm->height;j++)
    {
        for(int i=0;i<bm->width;i++)
        {
            memcpy(p+(i+x)*4+4*800*(j+y),bm->map+i*4+4*bm->width*j,3);
        }
    }
}

void *content1(void *arg)
// 线程函数，用于显示滚动提示信息，如“温馨提示：非必要不出门，出门戴口罩！”
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
     
    int lcd=open("/dev/fb0",O_RDWR);
 
    // 申请一块映射内存
    // char *p=calloc(1,size(lcd));
    char *p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));
 
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(50,380,4,0x000000ff);//宽、高、像素位数、
    int n=0;
    while(1)
    {
        fontSetSize(f, 50);//字库与字体大小
        fontPrint(f,bm,0,430-n%1150,"通知：广告位招租 广告位招租",0x0000ff00,50);     
        showbitmap(bm,p, 750, 50);
        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);        
        n++;
         
        if(n%1150==0)
        {
            n = 0;
        }
        usleep(100);
    }
}

void *content2(void *arg)
// 线程函数，用于显示滚动提示信息，如“温馨提示：非必要不出门，出门戴口罩！”
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
     
    int lcd=open("/dev/fb0",O_RDWR);
 
    // 申请一块映射内存
    // char *p=calloc(1,size(lcd));
    char *p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));
 
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(700,50,4,0x000000ff);//宽、高、像素位数、
    int n=0;
    while(1)
    {
        fontSetSize(f, 50);//字库与字体大小
        fontPrint(f,bm,800-n%1700,0,"温馨提示:非必要不出门，出门戴口罩！",0x0000ff00,900);     
        showbitmap(bm,p,0,430);
        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);        
        n++;

        if(n %1700 == 0)
        {       
            usleep(100);
            n=0;
            a=1;
        }
        pthread_testcancel(); 
    }
}
 
void *content3(void *arg)
// 线程函数，用于显示多行滚动文本，如“紧跟党走”、“永不变心”等
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
     
    int lcd=open("/dev/fb0",O_RDWR);
 
    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));
 
    //2.设置字体的大小a
    fontSetSize(f, 60);
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(750,70,4,0x000000ff);//宽、高、像素位数、
    int n=0;
    while(1)
    {
        if(n<300)
        {
            fontPrint(f,bm,n%300-240,0,"紧跟党走",0xff000000,400);     
            showbitmap(bm,p, 0, 80);
            bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        }
        if(n<420)
        {
            fontPrint(f,bm,n%420-240,0,"永不变心",0xff000000,400);     
            showbitmap(bm,p, 0, 150);
            bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        }
        if(n<540)
        {
            fontPrint(f,bm,n%540-240,0,"勤劳俭朴",0xff000000,400);     
            showbitmap(bm,p, 0, 220);
            bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        }
        if(n<660)
        {
            fontPrint(f,bm,n%660-240,0,"乐于助人",0xff000000,400);     
            showbitmap(bm,p, 0, 290);
            bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        }
        n+=10;
        usleep(100);
        if(a == 1)  //优化动态效果，使其大致同步
        {
            usleep(100*100);
            n=0;
            a=0;
        }
    }
}
 
 
void *days(void *arg)
// 线程函数，用于显示当前日期和时间
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
     
    int lcd=open("/dev/fb0",O_RDWR);
 
    // 申请一块映射内存
    char *p=calloc(1,size(lcd));
    p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));
 
    //2.设置字体的大小
    fontSetSize(f, 40);
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(800,50,4,0x000000ff);//宽、高、像素位数、
 
    struct timeval tv;
    time_t time;
    struct tm *p_time;
     
    char a[7][4]={"天","一","二","三","四","五","六"};
    int year;
    while(1)
    {   
        gettimeofday(&tv,NULL);//获取系统时间，并保存到tv中，注意这里可以精确到微秒
        time=tv.tv_sec;
        p_time=localtime(&time);//将系统时间转换为本地时间，考虑到本地时区和夏令时标志
        year=p_time->tm_year+1900;
        char buf[100]={0};
        sprintf(buf,"%d年%d月%d日 星期%s %d点%d分%2d秒",
                year,p_time->tm_mon+1,p_time->tm_mday,a[p_time->tm_wday],p_time->tm_hour,p_time->tm_min,p_time->tm_sec);
        
        //4.把字体输出到输出框中   
        fontPrint(f, bm, 10, 5,buf, 0x00ff0000, 800);
 
        //5.将bm妥善地放置到LCD上显示出来
 
        showbitmap(bm,p, 0, 0);
 
        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);
        usleep(1000);
    }
}

void *content4(void *arg)
// 线程函数，用于显示滚动提示信息，如“温馨提示：非必要不出门，出门戴口罩！”
{
    font *f = fontLoad("simfang.ttf"); // 指定字库文件，比如simfang.ttf
     
    int lcd=open("/dev/fb0",O_RDWR);
 
    // 申请一块映射内存
    char *p = mmap(NULL, size(lcd),PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0); 
    bzero(p,size(lcd));
 
    //3.设置字体输出框的大小
    bitmap *bm;       
    bm=createBitmapWithInit(100,50,4,0x000000ff);//宽、高、像素位数、

    while(1)
    {
        fontSetSize(f, 25);//字库与字体大小
        fontPrint(f,bm,0,0,"左滑退出",0x00ff0000,100);     
        showbitmap(bm,p,700,440);
        bzero(bm->map,bm->height*bm->width*bm->byteperpixel);   

        pthread_testcancel();
    }
}